package proyectoX.src.aplicacion;



import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;

import proyectoX.src.armas.Barra;
import proyectoX.src.entidades.Basica;
import proyectoX.src.entidades.Enemiga;
import proyectoX.src.entidades.Principal;
import proyectoX.src.entidades.Transporte;
import proyectoX.src.entidades.exploradora;

import proyectoX.src.fabricas.FactoryNivel;
import proyectoX.src.fabricas.FactoryPowerups;
import proyectoX.src.fabricas.FactoryPrimerNivel;
import proyectoX.src.municiones.MunicionBarra;
import proyectoX.src.municiones.MunicionEspejo;

import proyectoX.src.municiones.MunicionX;
import proyectoX.src.powerups.Power_up;

import java.util.Random;
import java.util.Vector;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Point;


/**
 * Nivel donde sucede toda la acción del juego. En esta clase es donde se determina la posición y el dibujo
 * de las componentes gráficas que aprticipan en el juego.
 * 
 * Mantiene referencias a una fábrica de powerups y a una fábrica de nivel que corresponde a la lógica 
 * de cómo se desarrolla el nivel.
 *
 */
public class Nivel extends JPanel {

	protected int puntajeParcial;
	protected Random generador;	
	protected Juego proyectoX;
	protected Principal naveJugador;	
	protected JLabel fondo;
	protected boolean congelado;

	protected FactoryPowerups fabricaPowerups;
	protected FactoryNivel fabricaNivel;
	protected Barra barra;

	/**
	 * Recibe el objeto Juego donde se está ejecutando el juego. Inicializa las fábricas y crea el fondo
	 * donde se irán posicionando las naves, municiones y powerups. 
	 * 
	 * @param j El objeto Juego donde se está ejecutando el juego.
	 */
	public Nivel(Juego j) {
		super();
		proyectoX = j;		
		this.setLayout(null);
		this.setVisible(true);
		this.setBounds(0, 0, j.getWidth()-j.getWidthInterfazIconos(), j.getHeight());

		fabricaPowerups = new FactoryPowerups(j);
		fabricaNivel = new FactoryPrimerNivel(j);

		generador = new Random();		
		barra = new Barra(this);

		//el fondo pasa a ser el lugar donde se colocarán todas las otras componentes gráficas 
		//así quedan encima del fondo		
		ImageIcon imgFondo = new ImageIcon(this.getClass().getResource("/proyectoX/sprites/space500.png"));
		fondo = new JLabel(imgFondo);
		fondo.setBounds(0, 0, j.getWidth()-j.getWidthInterfazIconos(), j.getHeight());
		this.add(fondo);		
		this.crearPrincipal();
		puntajeParcial=0;
		congelado=false;
	}


	/**
	 * Le pide a Juego que cree la nave del jugador y la coloca en su posición inicial.
	 */
	private void crearPrincipal() {		
		naveJugador = proyectoX.crearPrincipal(this);

		inicializarPosicion();		
		fondo.add(naveJugador);
	}

	/**
	 * Coloca a la nave del jugador en su posición inicial.
	 */
	public void inicializarPosicion(){
		naveJugador.setLocation((proyectoX.getWidth()-naveJugador.getWidth())/2, proyectoX.getHeight()-naveJugador.getHeight()-50);
		proyectoX.limpiarTeclas(); //resetear teclas apretadas
	}

	/**
	 * Le pide al objeto Juego que elimine la nave enemiga pasada por parámtero. 
	 * 
	 * @param e Nave enemiga a eliminar.
	 */
	public void eliminarEnemiga(Enemiga e) {
		proyectoX.quitarEnemiga(e);
		fondo.remove(e);
	}

	/**
	 * Pide al objeto Juego que elimine a todas las naves enemigas.
	 */
	public void eliminarTodasLasEnemigas() {
		proyectoX.eliminarTodasLasNavesEnemigas();		
	}


	/**
	 * Una vez que se pierde, se procede a eliminar la nave del jugador y mostrar el mensaje de "GAME OVER".
	 */
	public void eliminarNavePrincipal() {
		proyectoX.GameOver();	
		ImageIcon imgFondo = new ImageIcon(this.getClass().getResource("/proyectoX/sprites/GO.png"));
		proyectoX.actualizarPuntaje(puntajeParcial);

		JLabel Puntaje= new JLabel("Su Puntaje: "+ puntajeParcial);
		Puntaje.setBounds(200, 250,200,200);
		Font f= new Font("ARIAL",18,18);		
		Puntaje.setFont(f);
		Puntaje.setForeground(Color.WHITE);
		JLabel mensaje= new JLabel("Presione 'r' para reiniciar");
		mensaje.setFont(f);
		mensaje.setBounds(280, 350,200,200);		

		fondo.removeAll(); //Con esto se elimina todo lo que había en el fondo antes de mostrar el mensaje

		fondo.setIcon(imgFondo);	
		fondo.add(Puntaje);	
		fondo.add(mensaje);		
	}
	
	
	/**
	 * Una vez que se gana, se procede a eliminar la nave del jugador y mostrar el mensaje de "VICTORY".
	 */
	public void jefeDerrotado() {
		proyectoX.victoria();	
		ImageIcon imgFondo = new ImageIcon(this.getClass().getResource("/proyectoX/sprites/victory.png"));
		proyectoX.actualizarPuntaje(puntajeParcial);

		JLabel Puntaje= new JLabel("Su Puntaje: "+ puntajeParcial);
		Puntaje.setBounds(200, 250,200,200);
		Font f= new Font("ARIAL",18,18);		
		Puntaje.setFont(f);
		Puntaje.setForeground(Color.WHITE);
		JLabel mensaje= new JLabel("Presione 'r' para reiniciar");
		mensaje.setFont(f);
		mensaje.setBounds(280, 350,200,200);		

		fondo.removeAll(); //Con esto se elimina todo lo que había en el fondo antes de mostrar el mensaje

		fondo.setIcon(imgFondo);	
		fondo.add(Puntaje);	
		fondo.add(mensaje);		
		repaint();
	}

	/**
	 * Devuelve la instancia de la nave del jugador.
	 * 
	 * @return	La nave del jugador.
	 */
	public Principal obtenerNavePrincipal() {
		return naveJugador; 		
	}

	/**
	 * Aumenta el puntaje del jugador en la cantidad pasada por parámetro.
	 * 
	 * @param punt Cantidad de puntos a incrementar.
	 */
	public void sumarPuntaje(int punt) {
		puntajeParcial+=punt;
		proyectoX.getInterfazIconos().actualizarPuntaje();
	}

	/**
	 * Le pide al objeto Juego que agregue la munición al juego.
	 * 
	 * @param mun Munición a agregar.
	 */
	public void agregarMunicion(MunicionX mun) {
		proyectoX.agregarMunicion(mun);
		fondo.add(mun);
	}


	/**
	 * Le pide a juego que elimine la munición pasada por parámetro.
	 * 
	 * @param mun Munición a eliminar.
	 */
	public void quitarMunicion(MunicionX mun) {
		proyectoX.quitarMunicion(mun);
		mun.setVisible(false);
		fondo.remove(mun);
	}

	/**
	 * Le pide al objeto Juego que agregue el powerup pasado por parámetro.
	 * @param p Powerup a agregar.
	 */
	public void agregarPowerup(Power_up p) {
		proyectoX.agregarPowerup(p);
		fondo.add(p.getIcono());
	}

	/**
	 * Le pide al objeto Juego que elimine el powerup pasado por parámetro.
	 * @param p Powerup a eliminar.
	 */
	public void quitarPowerup(Power_up p) {
		proyectoX.quitarPowerup(p);
		p.getIcono().setVisible(false);
		fondo.remove(p.getIcono());
	}

	/**
	 * Le pide a Juego que calcule colisiones entre la munición pasada por parámetro y las naves enemigas.
	 * Devuelve un booleano que representa el éxito de la colsión.
	 * 
	 * @param m Munición a colisionar.
	 * @return Verdadero si colisionó, falso en caso contrario.
	 */
	public boolean calcularColisionConEnemigas(MunicionX m){
		return proyectoX.calculoConEnemigas(m);
	}

	/**
	 * Le pide a Juego que calcule colisiones entre la munición pasada por parámetro y la nave jugadora.
	 * Devuelve un booleano que representa el éxito de la colsión.
	 * 
	 * @param m Munición a colisionar.
	 * @return Verdadero si colisionó, falso en caso contrario.
	 */
	public boolean calcularColisionConPrincipal(MunicionX m){
		return proyectoX.calculoConPrincipal(m);
	}

	/**
	 * Le pide a Juego que calcule colisiones entre el poerup pasado por parámetro y la nave del jugador.
	 * Devuelve un booleano que representa el éxito de la colsión.
	 * 
	 * @param p Powerup a colisionar.
	 * @return Verdadero si colisionó, falso en caso contrario.
	 */
	public boolean calcularColisionConPowerup(Power_up p){
		return proyectoX.calculoConPowerup(p);
	}

	/**
	 * Le pide a Juego que calcule colisiones entre la MunicionEspejo pasada por parámetro y las otras municiones.
	 * Devuelve un booleano que representa el éxito de la colsión.
	 * 
	 * @param m MunicionEspejo a colisionar.
	 * @return Verdadero si colisionó, falso en caso contrario.
	 */
	public boolean calcularColisionConMuniciones(MunicionEspejo m) {
		return proyectoX.calculoConMuniciones(m);	
	}

	/**
	 * Elimina visualmente del nivel a la componente pasada por parámetro.
	 * @param c Componente a eliminar.
	 */
	public void eliminarGraficamente(Component c) {
		c.setEnabled(false);
		fondo.remove(c);
		fondo.repaint();
	}

	/**
	 * Devuelve el puntaje del nivel.
	 * @return Puntaje del nivel.
	 */
	public int getPuntajeParcial() {
		return puntajeParcial;
	}

	/**
	 * Le pide al objeto Juego que inicialice los puntos de vida de la nave jugadora.
	 */
	public void inicializarPuntosDeVida() {
		proyectoX.getInterfazIconos().inicializarPuntosDeVida();
	}

	/**
	 * Le pide al objeto Juego que reduzca los puntos de vida de la nave jugadora en la cantidad
	 * suministrada por parámetro.
	 * 
	 * @param danio Puntos de vida a restar.
	 */
	public void quitarPuntosVida(int danio) {
		proyectoX.getInterfazIconos().quitarPuntosDeVida(danio);
	}


	/**
	 * Le pide al objeto Juego que actualice los puntos de vida de la nave jugadora en el panel derecho.
	 */
	public void actualizarVida() {
		proyectoX.getInterfazIconos().actualizarVida();
	}

	/**
	 * Aumenta el nivel de láser de la nave jugadora si ésta no ha alcanzado el nivel máximo. En caso contrario
	 * se aumenta en 1000 el puntaje.
	 */
	public void aumentarNivelLaser() {
		if(!naveJugador.alcanzoLimite()){
			naveJugador.aumentarNivelLaser();
			proyectoX.getInterfazIconos().actualizarNivelLaser();
		}
		else{
			sumarPuntaje(1000);
		}

	}

	/**
	 * Incrementa las municiones de habilidad especial de la nave jugadora en la cantidad declarada en el parámetro.
	 * @param c Cantidad de municiones que se van a agregar.
	 */
	public void aumentarMunicionesHabilidad(int c) {
		naveJugador.aumentarMunicionesHabilidad(c);
		proyectoX.getInterfazIconos().actualizarMunicionesHabilidad();
	}

	/**
	 * Incrementa las municiones del arma secundaria de la nave jugadora en la cantidad declarada en el parámetro.
	 * @param c Cantidad de municiones que se van a agregar.
	 */
	public void aumentarMunicionesArma(int c) {
		naveJugador.aumentarMunicionesArma(c);
		proyectoX.getInterfazIconos().actualizarMunicionesArma();
	}

	/**
	 * Incrementa las municiones de Bomba Devastadora de la nave jugadora en la cantidad declarada en el parámetro.
	 * @param c Cantidad de municiones que se van a agregar.
	 */
	public void aumentarMunicionesBomba(int c) {
		naveJugador.aumentarMunicionesBomba(c);
		proyectoX.getInterfazIconos().actualizarMunicionesBomba();
	}


	/**
	 * Incrementa los puntos de vida de la nave jugadora en la cantidad declarada en el parámetro.
	 * @param c Cantidad de puntos de vida que se van a agregar.
	 */
	public void agregarPuntosDeVida(int c) {
		int d = naveJugador.agregarPuntosDeVida(c);
		proyectoX.getInterfazIconos().agregarPuntosDeVida(d);
	}

	/**
	 * Incrementa la cantidad de vidas de la nave jugadora en uno.
	 */
	public void aumentarVidaPrincipal() {
		naveJugador.aumentarVida();
		proyectoX.getInterfazIconos().actualizarVida();
	}


	//crea el objeto que aparece gráficamente en el juego como barra
	/**
	 * Al tomar el powerup de barrera eléctrica, se crea una barrera debajo de la nave del jugador.
	 */
	public void crearBarra() {
		int x = naveJugador.getX();	
		int y = naveJugador.getY()+50;
		barra.disparar(new Point(x,y));
	}

	/**
	 *	Le pide al panel derecho que actualice su label correspondiente a la cantidad de municiones de habilidad especial. 
	 */
	public void actualizarMunicionesHabilidad() {
		proyectoX.getInterfazIconos().actualizarMunicionesHabilidad();
	}

	/**
	 *	Le pide al panel derecho que actualice su label correspondiente a la cantidad de municiones de arma secundaria. 
	 */
	public void actualizarMunicionesArma() {
		proyectoX.getInterfazIconos().actualizarMunicionesArma();
	}

	/**
	 *	Le pide al panel derecho que actualice su label correspondiente a la cantidad de municiones de Bomba Devastadora. 
	 */
	public void actualizarMunicionesBombaDevastadora() {
		proyectoX.getInterfazIconos().actualizarMunicionesBomba();
	}

	/**
	 * Devuelve el alto del frame del objeto Juego.
	 * @return Altura de Juego.
	 */
	public int getTamanoPantallaY(){
		return proyectoX.getHeight();
	}

	/**
	 * Devuelve el ancho del frame del objeto Juego.
	 * @return Ancho de Juego.
	 */
	public int getTamanoPantallaX(){
		return proyectoX.getWidth();
	}

	/**
	 * Setea en verdadero el atributo utilizado para congelar todo (habilidad de nave Grande).
	 */
	public void CongelarTodo() {
		congelado=true;

	}
	
	/**
	 * Setea en falso el atributo utilizado para congelar todo (habilidad de nave Grande).
	 */
	public void DescongelarCongelarTodo() {
		congelado=false;

	}

	/**
	 * Devuelve un booleano correspondiente al estado de congelamiento del nivel.
	 * @return Verdadero si está cogelado, falso en cas contrario.
	 */
	public boolean NivelCongelado() {
		return congelado;
	}

	/**
	 * Una vez destruida un Transporte éste siempre deja un powerup el cual es creado utilizando este método.
	 * @param x Posición x donde se va a crear el powerup.
	 * @param y Posición y donde se va a crear el powerup.
	 */
	public void crearPowerupTransporte(int x, int y) {
		int indice = generador.nextInt(7); //random para elegir un powerup al azar

		switch(indice) {

		case 0: {
			agregarPowerup(fabricaPowerups.crearPuntosDeVida(x, y));
			break;
		}
		case 1: {
			agregarPowerup(fabricaPowerups.crearVidaExtra(x, y));
			break;
		}
		case 2: {
			agregarPowerup(fabricaPowerups.crearMunicionesArma(x, y));
			break;
		}
		case 3: {
			agregarPowerup(fabricaPowerups.crearMunicionesBombaDevastadora(x, y));
			break;
		}
		case 4: {
			agregarPowerup(fabricaPowerups.crearMunicionesHabilidad(x, y));
			break;
		}
		case 5: {
			agregarPowerup(fabricaPowerups.crearNivelLaser(x, y));
			break;
		}
		case 6: {
			agregarPowerup(fabricaPowerups.crearBarra(x, y));
			break;
		}		
		}

	}

	/**
	 * Pide al objeto Juego que actualice la cantidad de naves enemigas destruidas por el jugador.
	 */
	public void actualizarBajas() {
		proyectoX.actualizarBajas();
	}

	/**
	 * Develve la fábrica de nivel.
	 * @return La fábrica de nvel.
	 */
	public FactoryNivel getFabricaNivel() {
		return fabricaNivel;
	}

	/**
	 * Agrega la nave enemiga pasada por parámetro al fondo del juego.
	 * @param e Nave enemiga a agregar.
	 */
	public void agregarEnemigaAFondo(Enemiga e) {
		fondo.add(e);
	}

	/**
	 * Devuelve la fábrica de powerups.
	 * @return La fábrica de powerups.
	 */
	public FactoryPowerups getFabricaPowerups() {
		return fabricaPowerups;
	}

	/**
	 * Le pide al objeto Juego que devuelva la nave enemiga más cercana al punto suministrado por parámetro.
	 * @param point Punto a analizar. 
	 * @return Nave enemiga más cercana al punto.
	 */
	public Enemiga obtenerMasCercana(Point point) {		
		return proyectoX.obtenerEnemigaCerca(point);
	}

	
	/**
	 * Al ganar se invoca a victoria() de Juego
	 */
	public void victoria() {
		proyectoX.victoria();
	}

}